﻿using System;
using System.Collections.Generic;
using FluentIoC.Helpers;

namespace FluentIoC.Configuration.Commands
{
    public class SetProperty
    {
        protected string _value;
        protected Type _service;
        protected bool _valueSet;
        protected RegisterProperties _registration;

        internal string Property { get; private set; }
        internal string Value
        {
            get
            {
                if(_valueSet)
                {
                    return _value;
                }

                throw new FluentIoCException(
                    "The value of the property {0} has not been set", Property
                );
            }
        }

        public SetProperty(string property, RegisterProperties registration)
        {
            Property = property;
            _registration = registration;
        }

        public RegisterProperties WithValue(object value)
        {
            _value = value.ToString();
            _valueSet = true;

            return _registration;
        }

        public RegisterProperties UseService(string serviceKey)
        {
            _value = ConvertToServiceKey(serviceKey);    
            _valueSet = true;

            return _registration;
        }

        public RegisterProperties UseService<T>()
        {
            _service = typeof(T);
            
            return _registration;
        }

        public void Configure(Dictionary<Type, string> services)
        {
            if(false == _valueSet && null != _service)
            {
                if(false == services.ContainsKey(_service))
                {
                    throw new FluentIoCException(
                        "The service {0} which the property {1} requires has not been registered", 
                        _service.Name,
                        Property
                    );
                }

                _value = ConvertToServiceKey(services[_service]);
                _valueSet = true;
            }
        }

        protected string ConvertToServiceKey(string key)
        {
            if(false == key.StartsWith("${"))
            {
                return "${" + key + "}";
            }
            return key;
        }
    }

    public class SetProperty<T> : SetProperty
    {
        public SetProperty(Func<T, object> setProperty, string serviceKey)
            :this(setProperty)
        {
            _value = ConvertToServiceKey(serviceKey);
            _valueSet = true;
        }

        public SetProperty(Func<T, object> setProperty)
            :base(setProperty.GetMethodName(), null)
        {
            var invocation = setProperty.GetInvocationInfomation();

            if(invocation.Method.Name.StartsWith("set_"))
            {
                if (invocation.Arguments.Length != 1)
                {
                    throw new FluentIoCException("Incorrect number of arguments, you can only set properties");
                }

                _value = invocation.Arguments[0].ToString();
                _valueSet = true;
            }
            else
            {
                _service = invocation.Method.ReturnType;
            }
        }   
    }
}
